﻿#include "pch.h"
#include "qgp/qgach.h"
#include "qgp/qgadt.h"

//////////////////////////////////////////////////////////////////////////
// 레이캐스트
KIMPL_SUBCLASS(qgRayCast, "RayCast");

//
qgRayCast::qgRayCast()
{
}

//
qgRayCast::~qgRayCast()
{
	kobj_unload(_cam);
}

//
bool qgRayCast::SetPoint(float x, float y)
{
	const qbViewPort& vp = qgRdh::Instance->GetViewport();

	_loc.Set(x, y);

	const tpl::Mat4& proj = _cam->GetProjTM();
	const tpl::Mat4& invv = _cam->GetInvViewTM();

	tpl::Vec3 v(
		(((((x - vp.loc.x)*2.0f / (float)vp.size.x) - 1.0f)) - proj._31) / proj._11,
		((-(((y - vp.loc.y)*2.0f / (float)vp.size.y) - 1.0f)) - proj._32) / proj._22,
		1.0f);

	_dir.x = v.x*invv._11 + v.y*invv._21 + v.z*invv._31;
	_dir.y = v.x*invv._12 + v.y*invv._22 + v.z*invv._32;
	_dir.z = v.x*invv._13 + v.y*invv._23 + v.z*invv._33;

	_org.x = invv._41;
	_org.y = invv._42;
	_org.z = invv._43;

	return true;
}

//
bool qgRayCast::SetPoint(const tpl::Quad& viewbound, float x, float y)
{
	tpl::Vec2 loc = viewbound.Location();
	tpl::Vec2 size = viewbound.Size();

	_loc.Set(x, y);

	const tpl::Mat4& proj = _cam->GetProjTM();
	const tpl::Mat4& invv = _cam->GetInvViewTM();

	tpl::Vec3 v(
		(((((x - loc.x)*2.0f / (float)size.x) - 1.0f)) - proj._31) / proj._11,
		((-(((y - loc.y)*2.0f / (float)size.y) - 1.0f)) - proj._32) / proj._22,
		1.0f);

	_dir.x = v.x*invv._11 + v.y*invv._21 + v.z*invv._31;
	_dir.y = v.x*invv._12 + v.y*invv._22 + v.z*invv._32;
	_dir.z = v.x*invv._13 + v.y*invv._23 + v.z*invv._33;

	_org.x = invv._41;
	_org.y = invv._42;
	_org.z = invv._43;

	return true;
}

//
tpl::Vec3 qgRayCast::GetLocation(float dist)
{
	return (_dir * dist) + _org;
}

//
tpl::Line3 qgRayCast::GetLine(float scale)
{
	return tpl::Line3(_org, (_dir * scale) + _org);
}

//
bool qgRayCast::IntersectTri(const tpl::Vec3& v0, const tpl::Vec3& v1, const tpl::Vec3& v2, float* distance)
{
	tpl::Vec3 edge[2], test[3];
	float u, v, det;

	edge[0] = v1 - v0;
	edge[1] = v2 - v0;

	test[0] = tpl::Vec3::Cross(_dir, edge[1]);
	det = tpl::Vec3::Dot(edge[0], test[0]);
	if (det < KM_EPSILON)
		return false;

	test[1] = _org - v0;
	u = tpl::Vec3::Dot(test[1], test[0]);
	if (u<0.0f || u>det)
		return false;

	test[2] = tpl::Vec3::Cross(test[1], edge[0]);
	v = tpl::Vec3::Dot(_dir, test[2]);
	if (v<0.0f || (u + v)>det)
		return false;

	if (distance)
	{
		*distance = tpl::Vec3::Dot(edge[1], test[2]);
		*distance *= (1.0f / det);
	}

	return true;
}


//////////////////////////////////////////////////////////////////////////
// 카메라
KIMPL_SUBCLASS(qgCamera, "Camera");

//
qgCamera::qgCamera()
{
	const qbViewPort vp = qgRdh::Instance->GetViewport();

	_proj.fov = KM_PI_HOH;
	_proj.aspect = vp.aspect;
	_proj.z.Set(1.0f, 100000.0f);

	_view.up.y = 1.0f;
	_view.at.z = 1.0f;
	_view.ah.z = 1.0f;

	_camp.proj = tpl::Mat4::PerspectiveFovLH(_proj.fov, _proj.aspect, _proj.z.min, _proj.z.max);
	_camp.view.Identify();
	_camp.invv.Identify();
	_camp.vipr = _camp.proj;
}

//
qgCamera::~qgCamera()
{
}

//
bool qgCamera::Update(float adv)
{
	if (_insts.layout)
	{
		const qbViewPort vp = qgRdh::Instance->GetViewport();

		_proj.aspect = vp.aspect;
		_camp.proj = tpl::Mat4::PerspectiveFovLH(_proj.fov, _proj.aspect, _proj.z.min, _proj.z.max);

		UpdateTM();
		_camp.view = tpl::Mat4::LookAtLH(_trfm.loc, _view.at, _view.up);
		_camp.invv = _camp.view.Inverse();
		_camp.vipr = _camp.view * _camp.proj;
	}
	else if (UpdateTM())
	{
		_camp.view = tpl::Mat4::LookAtLH(_trfm.loc, _view.at, _view.up);
		_camp.invv = _camp.view.Inverse();
		_camp.vipr = _camp.view * _camp.proj;
	}

	UpdateSub(adv);

	return true;
}

//
void qgCamera::SetProj(float fov, float znear, float zfar)
{
	_proj.fov = fov;
	_proj.z.Set(znear, zfar);

	_camp.proj = tpl::Mat4::PerspectiveFovLH(_proj.fov, _proj.aspect, _proj.z.min, _proj.z.max);
	NeedUpdate();
}

//
void qgCamera::SetProj(float aspect, float fov, float znear, float zfar)
{
	_proj.fov = fov;
	_proj.aspect = aspect;
	_proj.z.Set(znear, zfar);

	_camp.proj = tpl::Mat4::PerspectiveFovLH(_proj.fov, _proj.aspect, _proj.z.min, _proj.z.max);
	NeedUpdate();
}

//
void qgCamera::SetView(const tpl::Vec3& at, const tpl::Vec3& ahead)
{
	SetViewPos(at);
	SetViewAhead(ahead);
}

//
void qgCamera::SetViewPos(const tpl::Vec3& at)
{
	_view.at = at;
	NeedUpdate();
}

//
void qgCamera::SetViewAhead(const tpl::Vec3& ahead)
{
	_view.ah = ahead;
	NeedUpdate();
}

//
void qgCamera::SetLayoutWork(bool iswork)
{
	_insts.layout = iswork;
}

//
float qgCamera::GetDistanceSq(const tpl::Vec3& v)
{
	return v.x*_camp.view._13 + v.y*_camp.view._23 + v.z*_camp.view._33 + _camp.view._43;
}

//
float qgCamera::GetDistance(const tpl::Vec3& v)
{
	float sq = v.x*_camp.view._13 + v.y*_camp.view._23 + v.z*_camp.view._33 + _camp.view._43;

	return k_sqrt(sq);
}

//
tpl::Vec3 Vec3_TransformCoord(const tpl::Vec3& left, const tpl::Mat4& right)
{
	float x, y, z, w;

	x = left.x * right._11 + left.y * right._21 + left.z * right._31 + right._41;
	y = left.x * right._12 + left.y * right._22 + left.z * right._32 + right._42;
	z = left.x * right._13 + left.y * right._23 + left.z * right._33 + right._43;
	w = left.x * right._14 + left.y * right._24 + left.z * right._34 + right._44;

	return tpl::Vec3(x / w, y / w, z / w);
}

//
tpl::Vec3 qgCamera::Project(const tpl::Vec3& v, const tpl::Mat4& world)
{
	tpl::Mat4 m = world;
	m *= _camp.view;
	m *= _camp.proj;

	tpl::Vec3 t = Vec3_TransformCoord(v, m);

	const qbViewPort vp = qgRdh::Instance->GetViewport();

	tpl::Vec3 r;
	r.x = vp.loc.x + (1.0f + t.x) * vp.size.width / 2.0f;
	r.y = vp.loc.y + (1.0f - t.y) * vp.size.height / 2.0f;
	r.z = vp.depth.min + t.z * (vp.depth.max - vp.depth.min);

	return r;
}

//
qgRayCast* qgCamera::CreateRayCast()
{
	qgRayCast* rc = kobj_create(qgRayCast);

	rc->_cam = this;
	this->Load();

	return rc;
}

//
qgCamera* qgCamera::New()
{
	qgCamera* self = kobj_create(qgCamera);

	return self;
}


//////////////////////////////////////////////////////////////////////////
// FPS 카메라
KIMPL_SUBCLASS(qgFpsCamera, "FpsCamera");

//
qgFpsCamera::qgFpsCamera()
	: _spdmov(100.0f), _spdang(1.0f)
{
	_insts.fps = true;
	_insts.can_fly = true;
	_insts.can_turn = true;

	_rot.Identify();
	_vrot.Identify();
	_mov.Identify();
	_ang.Identify();

	_keys[QSFPSWMV_FORWARD] = QIK_W;
	_keys[QSFPSWMV_BACKWARD] = QIK_S;
	_keys[QSFPSWMV_LEFT] = QIK_A;
	_keys[QSFPSWMV_RIGHT] = QIK_D;
	_keys[QSFPSWMV_UP] = QIK_R;
	_keys[QSFPSWMV_DOWN] = QIK_F;
	_keys[QSFPSWTN_UP] = QIK_UP;
	_keys[QSFPSWTN_DOWN] = QIK_DOWN;
	_keys[QSFPSWTN_LEFT] = QIK_LEFT;
	_keys[QSFPSWTN_RIGHT] = QIK_RIGHT;
}

//
qgFpsCamera::~qgFpsCamera()
{

}

//
bool qgFpsCamera::Update(float adv)
{
	if (_insts.layout)
	{
		const qbViewPort vp = qgRdh::Instance->GetViewport();

		_proj.aspect = vp.aspect;
		_camp.proj = tpl::Mat4::PerspectiveFovLH(_proj.fov, _proj.aspect, _proj.z.min, _proj.z.max);
	}

	if (_insts.can_turn)
	{
		_rot = tpl::Mat4::Rotation(_vrot);
		_view.up = tpl::Vec3::TransformCoord(tpl::Vec3(0.0f, 1.0f, 0.0f), _rot);
		_view.ah = tpl::Vec3::TransformCoord(tpl::Vec3(0.0f, 0.0f, 1.0f), _rot);

		if (!_insts.can_fly)
			_rot = tpl::Mat4::Rotation(tpl::Vec3(0.0f, _vrot.y, 0.0f));

		_view.at = _trfm.loc + _view.ah;
	}

	UpdateTM();
	_camp.view = tpl::Mat4::LookAtLH(_trfm.loc, _view.at, _view.up);
	_camp.invv = _camp.view.Inverse();
	_camp.vipr = _camp.view * _camp.proj;

	UpdateSub(adv);

	if (!_insts.can_turn)
		_insts.can_turn = true;

	return true;
}

//
void qgFpsCamera::SetRotation(const tpl::Quat& q)
{
	_vrot = tpl::Vec3::Rotation(q);
	super_type::SetRotation(q);
}

//
void qgFpsCamera::SetRotation(const tpl::Vec3& v)
{
	_vrot = v;
	_trfm.rot = tpl::Quat::Rotation(tpl::Mat4::Rotation(v));

	NeedUpdate();
}

//
void qgFpsCamera::RefreshAngle(const tpl::Vec3& ang, float delta)
{
	_lang = ang;

	_vrot += ang * (_spdang * delta);
	_vrot.x = K_MAX(-KM_PI_2, _vrot.x);
	_vrot.x = K_MIN(+KM_PI_2, _vrot.x);

	_trfm.rot = tpl::Quat::Rotation(tpl::Mat4::Rotation(_vrot));

	_ang.Identify();

	NeedUpdate();
}

//
void qgFpsCamera::RefreshAngle(float delta)
{
	RefreshAngle(_ang, delta);
}

//
void qgFpsCamera::RefreshMove(const tpl::Vec3& move, float delta)
{
	_lmov = move;

	_trfm.loc += tpl::Vec3::TransformCoord(move* (_spdmov * delta), _rot);

	_mov.Identify();

	NeedUpdate();
}

//
void qgFpsCamera::RefreshMove(float delta)
{
	RefreshMove(_mov, delta);
}

//
void qgFpsCamera::SetFly(bool v)
{
	_insts.can_fly = v;
}

//
void qgFpsCamera::SetMoveSpeed(float v)
{
	_spdmov = v;
}

//
void qgFpsCamera::SetAngleSpeed(float v)
{
	_spdang = v;
}

//
void qgFpsCamera::SetKey(qgFpsWhere w, kbyte k)
{
	if (w >= QSFPSWMV_FORWARD && w <= QSFPSWTN_DOWN)
		_keys[w] = k;
}

//
void qgFpsCamera::MovePoint(float x, float y, float adv)
{
	if (adv < 0.0f)
		_pt[1].Set(x, y);
	else
	{
		_pt[0].x = x - _pt[1].x;
		_pt[0].y = y - _pt[1].y;
		_pt[1].Set(x, y);

		tpl::Vec3 v((float)_pt[0].y, (float)_pt[0].x, 0.0f);
		RefreshAngle(v, adv);
	}
#if 0
	hbbool ismove;

	else
	{
		ismove = FALSE;

#if 0
		// 패드...
		rqPadInput cs;

		for (ismove = i = 0; i < RQCAM_WHERE_MAX_VALUE; i++)
		{
			if (rqInDev_TestKey(_keys[i]))
			{
				ismove++;
				qs_fpscam_Move(self, i);
			}
		}

		if (rqInDev_PadInput(HGIP_AT_1, &cs))
		{
			if (cs.leftx != 0)
			{
				_mov.x += cs.leftx / 32768.0f;
				ismove++;
			}
			if (cs.lefty != 0)
			{
				_mov.z += cs.lefty / 32768.0f;
				ismove++;
			}

			if (cs.rightx != 0)
			{
				_ang.y += cs.rightx / 32768.0f;
				ismove++;
			}
			if (cs.righty != 0)
			{
				_ang.x -= cs.righty / 32768.0f;
				ismove++;
			}
		}

		if (ismove)
		{
			qs_fpscam_refresh_move(self, NULL, adv);
			qs_fpscam_refresh_angle(self, NULL, adv);
		}
#endif
	}

	return ismove;
#endif
}

//
void qgFpsCamera::MoveKey(qgFpsWhere w)
{
	switch (w)
	{
		case QSFPSWMV_FORWARD:		_mov.z += 1.0f;	break;
		case QSFPSWMV_BACKWARD:		_mov.z -= 1.0f;	break;
		case QSFPSWMV_LEFT:			_mov.x -= 1.0f;	break;
		case QSFPSWMV_RIGHT:		_mov.x += 1.0f;	break;
		case QSFPSWMV_UP:			_mov.y += 1.0f;	break;
		case QSFPSWMV_DOWN:			_mov.y -= 1.0f;	break;

		case QSFPSWTN_LEFT:			_ang.y -= 1.0f;	break;
		case QSFPSWTN_RIGHT:		_ang.y += 1.0f;	break;
		case QSFPSWTN_UP:			_ang.x -= 1.0f;	break;
		case QSFPSWTN_DOWN:			_ang.x += 1.0f;	break;
	}
}

//
qgFpsCamera* qgFpsCamera::New()
{
	qgFpsCamera* self = kobj_create(qgFpsCamera);

	return self;
}


//////////////////////////////////////////////////////////////////////////
// 마야 카메라
KIMPL_SUBCLASS(qgMayaCamera, "MayaCamera");

//
qgMayaCamera::qgMayaCamera()
	: _dist(200.0f)
	, _spdmov(1.0f)
	, _spdang(1.0f)
{
	_insts.maya = true;
	_insts.can_turn = true;

	_rot.Identify();
	_vrot.Identify();
	_mov.Identify();
	_ang.Identify();

	_keys[QSFPSWMV_FORWARD] = QIK_W;
	_keys[QSFPSWMV_BACKWARD] = QIK_S;
	_keys[QSFPSWMV_LEFT] = QIK_A;
	_keys[QSFPSWMV_RIGHT] = QIK_D;
	_keys[QSFPSWMV_UP] = QIK_R;
	_keys[QSFPSWMV_DOWN] = QIK_F;
	_keys[QSFPSWTN_UP] = QIK_UP;
	_keys[QSFPSWTN_DOWN] = QIK_DOWN;
	_keys[QSFPSWTN_LEFT] = QIK_LEFT;
	_keys[QSFPSWTN_RIGHT] = QIK_RIGHT;
}

//
qgMayaCamera::~qgMayaCamera()
{

}

//
bool qgMayaCamera::Update(float adv)
{
	if (_insts.layout)
	{
		const qbViewPort vp = qgRdh::Instance->GetViewport();

		_proj.aspect = vp.aspect;
		_camp.proj = tpl::Mat4::PerspectiveFovLH(_proj.fov, _proj.aspect, _proj.z.min, _proj.z.max);
	}

	if (_insts.can_turn)
	{
		_rot = tpl::Mat4::Rotation(_vrot);
		_view.up = tpl::Vec3::TransformCoord(tpl::Vec3(0.0f, 1.0f, 0.0f), _rot);
		_view.ah = tpl::Vec3::TransformCoord(tpl::Vec3(0.0f, 0.0f, 1.0f), _rot);
		_trfm.loc = _view.at + _view.ah;
	}

	UpdateTM();

	tpl::Vec3 ad = _view.ah * _dist;
	tpl::Vec3 ne = _view.at + ad;
	tpl::Vec3 na = ne - _view.ah;

	_camp.view = tpl::Mat4::LookAtLH(ne, na, _view.up);
	_camp.invv = _camp.view.Inverse();
	_camp.vipr = _camp.view * _camp.proj;

	UpdateSub(adv);

	if (!_insts.can_turn)
		_insts.can_turn = true;

	return true;
}

//
void qgMayaCamera::SetRotation(const tpl::Quat& q)
{
	_vrot = tpl::Vec3::Rotation(q);
	super_type::SetRotation(q);
}

//
void qgMayaCamera::SetRotation(const tpl::Vec3& v)
{
	_vrot = v;
	_trfm.rot = tpl::Quat::Rotation(tpl::Mat4::Rotation(v));

	NeedUpdate();
}

//
void qgMayaCamera::RefreshAngle(const tpl::Vec3& angle, float delta)
{
	_lang = angle;

	_vrot += angle*(_spdang*delta);
	_vrot.x = K_MAX(-KM_PI_2, _vrot.x);
	_vrot.x = K_MIN(+KM_PI_2, _vrot.x);
	_vrot.z = k_cprad(_vrot.z);
	_vrot.y = k_cprad(_vrot.y);

	_trfm.rot = tpl::Quat::Rotation(tpl::Mat4::Rotation(_vrot));

	_ang.Identify();

	NeedUpdate();
}

//
void qgMayaCamera::RefreshAngle(float delta)
{
	RefreshAngle(_ang, delta);
}

//
void qgMayaCamera::RefreshMove(const tpl::Vec3& move, float delta)
{
	_lmov = move;

	_view.at += tpl::Vec3::TransformCoord(move*(_spdmov*delta), _rot);

	_mov.Identify();

	NeedUpdate();
}

//
void qgMayaCamera::RefreshMove(float delta)
{
	RefreshMove(_mov, delta);
}

//
void qgMayaCamera::SetMoveSpeed(float v)
{
	_spdmov = v;
}

//
void qgMayaCamera::SetAngleSpeed(float v)
{
	_spdang = v;
}

//
void qgMayaCamera::SetKey(qgFpsWhere w, kbyte k)
{
	if (w >= QSFPSWMV_FORWARD && w <= QSFPSWTN_DOWN)
		_keys[w] = k;
}

//
void qgMayaCamera::MovePoint(float x, float y, float adv)
{
	if (adv < 0.0f)
		_pt[1].Set(x, y);
	else
	{
		_pt[0].x = x - _pt[1].x;
		_pt[0].y = y - _pt[1].y;
		_pt[1].Set(x, y);

		tpl::Vec3 v((float)_pt[0].y, (float)_pt[0].x, 0.0f);
		RefreshAngle(v, adv);
	}
#if 0
	hbbool ismove;

	else
	{
		ismove = FALSE;

#if 0
		// 패드...
		rqPadInput cs;

		for (ismove = i = 0; i < RQCAM_WHERE_MAX_VALUE; i++)
		{
			if (rqInDev_TestKey(_keys[i]))
			{
				ismove++;
				qs_fpscam_Move(self, i);
			}
		}

		if (rqInDev_PadInput(HGIP_AT_1, &cs))
		{
			if (cs.leftx != 0)
			{
				_mov.x += cs.leftx / 32768.0f;
				ismove++;
			}
			if (cs.lefty != 0)
			{
				_mov.z += cs.lefty / 32768.0f;
				ismove++;
			}

			if (cs.rightx != 0)
			{
				_ang.y += cs.rightx / 32768.0f;
				ismove++;
			}
			if (cs.righty != 0)
			{
				_ang.x -= cs.righty / 32768.0f;
				ismove++;
			}
		}

		if (ismove)
		{
			qs_fpscam_refresh_move(self, NULL, adv);
			qs_fpscam_refresh_angle(self, NULL, adv);
		}
#endif
	}

	return ismove;
#endif
}

//
void qgMayaCamera::MoveKey(qgFpsWhere w)
{
	switch (w)
	{
		case QSFPSWMV_FORWARD:		_mov.z += 1.0f;	break;
		case QSFPSWMV_BACKWARD:		_mov.z -= 1.0f;	break;
		case QSFPSWMV_LEFT:			_mov.x -= 1.0f;	break;
		case QSFPSWMV_RIGHT:		_mov.x += 1.0f;	break;
		case QSFPSWMV_UP:			_mov.y += 1.0f;	break;
		case QSFPSWMV_DOWN:			_mov.y -= 1.0f;	break;

		case QSFPSWTN_LEFT:			_ang.y -= 1.0f;	break;
		case QSFPSWTN_RIGHT:		_ang.y += 1.0f;	break;
		case QSFPSWTN_UP:			_ang.x -= 1.0f;	break;
		case QSFPSWTN_DOWN:			_ang.x += 1.0f;	break;
	}
}

//
qgMayaCamera* qgMayaCamera::New()
{
	qgMayaCamera* self = kobj_create(qgMayaCamera);

	return self;
}


